home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / programr / wincap.zip / WINCAP.C < prev    next >
C/C++ Source or Header  |  1991-11-05  |  30KB  |  933 lines

  1. /*
  2.  * Wincap.c
  3.  *
  4.  * Windows Screen Capture Utility
  5.  * Version 3.00
  6.  *
  7.  * Description:
  8.  * ------------
  9.  *
  10.  * Captures portions of the screen, specific windows, or the entire screen
  11.  * and saves it to a file or prints it.  Uses DIBAPI functions to do most
  12.  * of the capture/printing/saving work.  See the file DIBAPI.TXT for a
  13.  * description of the DIB api functions.
  14.  *
  15.  * Development Team: Mark Bader
  16.  *                   Patrick Schreiber
  17.  *                   Garrett McAuliffe
  18.  *                   Eric Flo
  19.  *                   Tony Claflin
  20.  *
  21.  * Written by Microsoft Product Support Services, Developer Support.
  22.  * Copyright (c) 1991 Microsoft Corporation. All rights reserved.
  23.  */
  24. #include <windows.h>
  25. #include <string.h>
  26. #include "wincap.h"
  27. #include "dialogs.h"
  28. #include "dibapi.h"
  29. #include "errors.h"
  30.  
  31. char szAppName[20];     // Name of application - used in dialog boxes
  32.  
  33. /* Global variables */
  34. HWND ghInst;            /* Handle to instance */
  35. HWND ghWndMain;         /* Handle to main window */
  36. FARPROC lpfnKeyHook;    // Used in keyboard hook
  37. FARPROC lpfnOldHook;    // Used for keyboard hook
  38. HWND hModelessDlg;      // Handle to modeless dialog box
  39.  
  40. /* Macro to swap two values */
  41. #define SWAP(x,y)   ((x)^=(y)^=(x)^=(y))
  42.  
  43.  
  44. /**************************************************************************
  45.  *
  46.  * WinMain()
  47.  *
  48.  * Entry point of our Application.
  49.  *
  50.  *************************************************************************/
  51.  
  52.  
  53. int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdLine,
  54.                    int nCmdShow)
  55. {
  56.    MSG msg;
  57.    WNDCLASS wndclass;
  58.    HWND hWnd;
  59.  
  60.    strcpy(szAppName, "WinCap");     // Name of our App
  61.    hModelessDlg = NULL;             // Set handle to modeless dialog to NULL because
  62.                                     // we haven't created it yet
  63.    if (!hPrevInstance)
  64.    {
  65.       wndclass.style = 0;
  66.       wndclass.lpfnWndProc = WndProc;
  67.       wndclass.cbClsExtra = 0;
  68.       wndclass.cbWndExtra = 0;
  69.       wndclass.hInstance = hInstance;
  70.       wndclass.hIcon = LoadIcon(hInstance, "WINCAP");
  71.       wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
  72.       wndclass.hbrBackground = COLOR_WINDOW + 1;
  73.       wndclass.lpszMenuName = (LPSTR)NULL;
  74.       wndclass.lpszClassName = (LPSTR)szAppName;
  75.       if (!RegisterClass(&wndclass))
  76.          return FALSE;
  77.       ghInst = hInstance;  // Set Global variable
  78.  
  79.       /*
  80.        * Create a main window for this application instance - but don't
  81.        * display it.
  82.        */
  83.       hWnd = CreateWindow(szAppName,            // Name of the window's class
  84.                           "Screen Capture",     // Text for window caption
  85.                           WS_OVERLAPPEDWINDOW,  // Window Style
  86.                           CW_USEDEFAULT,        // Default horizontal position
  87.                           CW_USEDEFAULT,        // Default vertical position
  88.                           CW_USEDEFAULT,        // Default width
  89.                           CW_USEDEFAULT,        // Default height
  90.                           NULL,                 // Overlapped windows have no parent
  91.                           NULL,                 // Use the window class menu
  92.                           hInstance,            // This instance owns this window
  93.                           NULL);                // Pointer (not used)
  94.       ghWndMain = hWnd;      // Set global variable
  95.  
  96.       /*
  97.        * We want to keep the window iconic, so let's make sure that it
  98.        * starts out iconic (by using SW_SHOWMINNOACTIVE), and we also
  99.        * trap the WM_QUERYOPEN message in our main message loop.  These
  100.        * two things together will keep our window iconic.
  101.        */
  102.       ShowWindow(hWnd, SW_SHOWMINNOACTIVE);
  103.       UpdateWindow(hWnd);
  104.  
  105.       /*
  106.        * Set up the Keyboard hook for our hotkey
  107.        */
  108.       lpfnKeyHook = MakeProcInstance((FARPROC)KeyboardHook, hInstance);
  109.       lpfnOldHook = SetWindowsHook(WH_KEYBOARD, lpfnKeyHook);
  110.    }
  111.  
  112.    /*
  113.     * If another instance of our program is already running, let the
  114.     * user know about it then exit.
  115.     */
  116.    if (hPrevInstance)
  117.    {
  118.       MessageBox(NULL, "WinCap is already running.  "
  119.                        "There is no need to invoke it twice.", szAppName,
  120.                  MB_OK | MB_ICONHAND);
  121.       return FALSE;
  122.    }
  123.  
  124.    /* Polling messages from event queue  */
  125.    while (GetMessage(&msg, NULL, 0, 0))
  126.    {
  127.       if (hModelessDlg == NULL || !IsDialogMessage(hModelessDlg, &msg))
  128.       {
  129.          TranslateMessage(&msg);
  130.          DispatchMessage(&msg);
  131.       }
  132.    }
  133.    return msg.wParam;
  134. }
  135.  
  136.  
  137. /***********************************************************************
  138.  *
  139.  * KeyboardHook()
  140.  *
  141.  * This is the Keyboard Hook function which windows will call every
  142.  * time it gets a keyboard message.  In this function, we check to
  143.  * see if the key pressed was Ctrl+Alt+F9, and if it is, we post
  144.  * the proper message to our main window which will call up the
  145.  * printscreen dialog box.
  146.  *
  147.  **********************************************************************/
  148.  
  149.  
  150. DWORD FAR PASCAL KeyboardHook(int iCode, WORD wParam, LONG lParam)
  151. {
  152.    if (iCode == HC_ACTION && wParam == VK_F9 && GetKeyState(VK_SHIFT) < 0 &&
  153.        GetKeyState(VK_CONTROL) < 0)
  154.    {
  155.       if (HIWORD (lParam) & 0x8000)
  156.          PostMessage(ghWndMain, WM_PRTSC, 0, 0L);
  157.       return TRUE;
  158.    }
  159.    else
  160.       return DefHookProc(iCode, wParam, lParam, &lpfnOldHook);
  161. }
  162.  
  163.  
  164. /***********************************************************************
  165.  *
  166.  * WndProc()
  167.  *
  168.  * This is our main window procedure.  It receives all the messages destined
  169.  * for our application's main window.
  170.  *
  171.  **********************************************************************/
  172.  
  173.  
  174. long FAR PASCAL WndProc(HWND hWnd, WORD wMessage, WORD wParam, LONG lParam)
  175. {
  176.    /*
  177.     * The bNowPrinting variable is set to TRUE if we are in the middle of
  178.     * printing.  This takes care of the case when the user presses the hotkey
  179.     * during capturing
  180.     */
  181.    static BOOL bNowPrinting = FALSE;
  182.    static HANDLE hOptionStruct;         // handle to pass OPTIONSTRUCT to dialog box
  183.  
  184.    switch (wMessage)
  185.       {
  186.    case WM_CREATE:
  187.    {
  188.       HANDLE hSysMenu;
  189.       FARPROC lpfnDIALOGSMsgProc;
  190.       LPOPTIONSTRUCT lpOptionStruct;
  191.  
  192.       /*
  193.        * Since we want our main window to stay iconized, the user will never
  194.        * see any menus we add to our main window, so instead let's add the
  195.        * menu items to the system menu, which can be called up by clicking
  196.        * once on the icon.
  197.        */
  198.  
  199.       hSysMenu = GetSystemMenu(hWnd, FALSE);
  200.       AppendMenu(hSysMenu, MF_SEPARATOR, 0, NULL);
  201.       AppendMenu(hSysMenu, MF_STRING, IDM_ABOUT, "&About WinCap...");
  202.       AppendMenu(hSysMenu, MF_STRING, IDM_CAPTURE, "Ca&pture Screen...");
  203.  
  204.       /*
  205.        * Put up InfoBox to let user know that we loaded.
  206.        */
  207.       lpfnDIALOGSMsgProc = MakeProcInstance((FARPROC)InfoBoxDlgProc, ghInst);
  208.       DialogBox(ghInst, (LPSTR)"InfoBox", hWnd, lpfnDIALOGSMsgProc);
  209.       FreeProcInstance(lpfnDIALOGSMsgProc);
  210.  
  211.       /*
  212.        * Allocate memory for an OPTIONSTRUCT.  This structure will be used to
  213.        * pass data to and receive data from the Options dialog box.
  214.        */
  215.       hOptionStruct = GlobalAlloc(GMEM_MOVEABLE, sizeof(OPTIONSTRUCT) + 5);
  216.  
  217.       /*
  218.        * Now set up default options
  219.        */
  220.       lpOptionStruct = (LPOPTIONSTRUCT)GlobalLock(hOptionStruct);
  221.       if (hOptionStruct)
  222.       {
  223.          lpOptionStruct->iOptionArea = IDC_SINGLEWINDOW;
  224.          lpOptionStruct->iOptionWindow = IDC_ENTIREWINDOW;
  225.          lpOptionStruct->iOptionDest = OPTION_PRINTER;
  226.          lstrcpy(lpOptionStruct->szFileName, (LPSTR)"c:\\capture.bmp");
  227.          lpOptionStruct->iOptionPrint = IDC_BESTFIT;
  228.          lpOptionStruct->iXScale = 1;
  229.          lpOptionStruct->iYScale = 2;
  230.          GlobalUnlock(hOptionStruct);
  231.       }
  232.    }
  233.       break;
  234.  
  235.    case WM_PRTSC:
  236.    {
  237.  
  238.    /*
  239.     * The WM_PRTSC message is one that we defined in our header file.  This
  240.     * message is sent to us when the user wants to capture the screen, either
  241.     * by hitting the hotkey (see the KeyboardHook procedure above), by
  242.     * double-clicking on the icon caption (see WM_QUERYOPEN message case
  243.     * below), or by selecting the menu item "Capture Screen..." (see
  244.     * WM_SYSCOMMAND message case below).
  245.     */
  246.  
  247.    /* Check to see that we aren't already in the middle of printing.
  248.     * This could happen if the user presses our hotkey in the middle of
  249.     * one of our dialog boxes.
  250.     */
  251.       if (bNowPrinting)
  252.       {
  253.          MessageBox(NULL, "Already capturing screen.", szAppName, MB_OK |
  254.                     MB_ICONEXCLAMATION);
  255.       }
  256.       else
  257.       {
  258.  
  259.          // Commence screen capture!
  260.          bNowPrinting = TRUE;
  261.          DoCapture(hOptionStruct);
  262.          bNowPrinting = FALSE;
  263.       }
  264.    } /* End WM_PRTSC case */
  265.       break;
  266.  
  267.    case WM_QUERYOPEN:
  268.  
  269.       /*
  270.        * This code makes the window stay iconic.  The PostMessage() is here
  271.        * so that when user double-clicks on icon, we do the printscreen.
  272.        */
  273.       PostMessage(hWnd, WM_PRTSC, 0, 0L);
  274.       return FALSE;
  275.  
  276.    case WM_SYSCOMMAND:
  277.       switch (wParam)
  278.          {
  279.       case IDM_ABOUT:
  280.  
  281.       /*
  282.        * Display "About" Box
  283.        */
  284.       {
  285.          FARPROC lpfnDIALOGSMsgProc;
  286.  
  287.          lpfnDIALOGSMsgProc = MakeProcInstance((FARPROC)AboutDlgProc, ghInst);
  288.          DialogBox(ghInst, (LPSTR)"About", hWnd, lpfnDIALOGSMsgProc);
  289.          FreeProcInstance(lpfnDIALOGSMsgProc);
  290.       }
  291.          return 0;
  292.  
  293.       case IDM_CAPTURE:
  294.  
  295.          /*
  296.           * User selected "Capture Screen..." From the menu
  297.           */
  298.          PostMessage(hWnd, WM_PRTSC, 0, 0L);
  299.          return 0;
  300.          }
  301.       return DefWindowProc(hWnd, wMessage, wParam, lParam);
  302.       break;
  303.  
  304.    case WM_DESTROY:
  305.  
  306.       /*
  307.        * Clean up
  308.        */
  309.       UnhookWindowsHook(WH_KEYBOARD, lpfnKeyHook);
  310.  
  311.       /*
  312.        * Free memory used for OPTIONSTRUCT
  313.        */
  314.       GlobalFree(hOptionStruct);
  315.       PostQuitMessage(0);
  316.       break;
  317.  
  318.    default:
  319.       return DefWindowProc(hWnd, wMessage, wParam, lParam);
  320.       }
  321.    return 0L;
  322. }
  323.  
  324.  
  325. /***********************************************************************
  326.  *
  327.  * DoCapture()
  328.  *
  329.  * This procedure gets called when the user wants to capture the
  330.  * screen.  This is where we actually bring up the proper dialog
  331.  * boxes and call the proper screen capture functions.
  332.  *
  333.  **********************************************************************/
  334.  
  335.  
  336. void DoCapture(HANDLE hOptionStruct)
  337. {
  338.    FARPROC lpfnDIALOGSMsgProc;     // Pointer for dialog boxes
  339.    int nResult;                    // return codes from the dialog boxes
  340.    LPOPTIONSTRUCT lpOptionStruct;  // Pointer to OPTIONSTRUCT
  341.    static HDIB hDib;               // Handle to our captured screen DIB
  342.    char szWindowText[100];         // Text which tells what we captured
  343.    static HWND hWndCurrent;
  344.  
  345.  
  346.    /*
  347.     * Keep track of the active window so we can bring it to top
  348.     * later.
  349.     */
  350.  
  351.    hWndCurrent = GetActiveWindow();
  352.  
  353.    /*
  354.     * Call up Options dialog box
  355.     */
  356.    lpfnDIALOGSMsgProc = MakeProcInstance((FARPROC)OptionsDlgProc, ghInst);
  357.  
  358.    /*
  359.     * Pass the handle to our OPTIONSTRUCT to the dialog box in the 5th parameter
  360.     * to DialogBoxParam.  This will be passed to our dialog box in the lParam
  361.     * WM_INITDIALOG message.  Since the 5th parameter to DialogBoxParam()
  362.     * is 32 bits, and our handle is only 16 bits, let's put it in the
  363.     * LOWORD() of the parameter.
  364.     */
  365.    nResult = DialogBoxParam(ghInst, (LPSTR)"Options", ghWndMain,
  366.                             lpfnDIALOGSMsgProc, (DWORD)MAKELONG(hOptionStruct,
  367.                             0));
  368.    FreeProcInstance(lpfnDIALOGSMsgProc);
  369.  
  370.    /*
  371.     * If user presses OK (nResult == TRUE), then go on, otherwise don't go on
  372.     */
  373.    if (nResult)
  374.    {
  375.       lpOptionStruct = (LPOPTIONSTRUCT)GlobalLock(hOptionStruct);
  376.       if (!lpOptionStruct)
  377.          return;
  378.  
  379.       /*
  380.        * The structure should now contain:
  381.        * 1. iOptionArea - Specifies which area user wants to capture
  382.        *    One of: IDC_SINGLEWINDOW
  383.        *            IDC_ENTIRESCREEN
  384.        *            IDC_PARTIALSCREEN
  385.        * 2. iOptionWindow - Specifies which portion of the window to capture -
  386.        *    this will only contain valid data if the iOptionArea is set to
  387.        *    IDC_SINGLEWINDOW.  Will be one of:
  388.        *            IDC_ENTIREWINDOW
  389.        *            IDC_CLIENTAREAONLY
  390.        *            IDC_CAPTIONBARONLY
  391.        *            IDC_MENUBARONLY
  392.        * 3. iOptionDest - Bitfield which specifies the destination of the
  393.        *    bitmap.  Can be a logical combination of: OPTION_FILE and
  394.        *    OPTION_PRINTER.
  395.        * 4. szFileName - string which contains the filename the user
  396.        *    typed into as the name to save the bitmap to.  This is only
  397.        *    valid if the iOptionDest has the OPTION_FILE bit set.
  398.        * 5. iOptionPrint - Print Options.  Will be one of:
  399.        *            IDC_BESTFIT
  400.        *            IDC_STRETCHTOPAGE
  401.        *            IDC_SCALE
  402.        * 6. iXScale, iYScale - X and Y scaling factors for printing bitmap.
  403.        *    These values are only valid if iOptionPrint is set to IDC_SCALE.
  404.        */
  405.       switch (lpOptionStruct->iOptionArea)
  406.          {
  407.       case IDC_ENTIRESCREEN:
  408.  
  409.       /*
  410.        * Copy Entire screen to DIB
  411.        */
  412.       {
  413.          RECT rScreen;       // Rect containing entire screen
  414.          HDC hDC;            // DC to screen
  415.          MSG msg;            // Message for the PeekMessage()
  416.  
  417.          hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
  418.          rScreen.left = rScreen.top = 0;
  419.          rScreen.right = GetDeviceCaps(hDC, HORZRES);
  420.          rScreen.bottom = GetDeviceCaps(hDC, VERTRES);
  421.          strcpy(szWindowText, "Entire screen");
  422.  
  423.          /* Bring the previous current window to the top of the Z-order.
  424.           * Wait until this application gets another message -- this allows
  425.           * the other windows (which were obscured before by the dialog
  426.           * box) to repaint themselves.
  427.           */
  428.          BringWindowToTop(hWndCurrent);
  429.          while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != 0)
  430.          {
  431.             TranslateMessage(&msg);
  432.             DispatchMessage(&msg);
  433.          }
  434.  
  435.          /*
  436.           * Call the DIB API function which captures the screen.  This
  437.           * will automatically allocate enough space for the DIB, fill
  438.           * in the proper fields in the DIB header, and return us a
  439.           * pointer to the memory containing the header, colortable,
  440.           * and bits of the DIB.
  441.           */
  442.          hDib = CopyScreenToDIB(&rScreen);
  443.       }
  444.          break;
  445.  
  446.       case IDC_PARTIALSCREEN:
  447.       /*
  448.        * Copy user-selected portion of screen to DIB
  449.        */
  450.       {
  451.          RECT rRubberBand;       // Region to capture (screen coordinates)
  452.  
  453.          /*
  454.           * Allow user to "rubberband" a section of the screen for
  455.           * us to capture
  456.           */
  457.  
  458.          RubberBandScreen(&rRubberBand);
  459.          strcpy(szWindowText, "User selected portion");
  460.  
  461.          /*
  462.           * Call the DIB API function which captures the screen.  This
  463.           * will automatically allocate enough space for the DIB, fill
  464.           * in the proper fields in the DIB header, and return us a
  465.           * pointer to the memory containing the header, colortable,
  466.           * and bits of the DIB.
  467.           */
  468.          hDib = CopyScreenToDIB(&rRubberBand);
  469.       }
  470.          break;
  471.  
  472.       case IDC_SINGLEWINDOW:
  473.       /*
  474.        * Allow the user to click on a single window to capture
  475.        */
  476.       {
  477.          HWND hWndSelect;
  478.          HWND hWndDesktop;
  479.          WORD wOption;
  480.  
  481.          /*
  482.           * Call function which lets user select a window
  483.           */
  484.  
  485.          hWndSelect = SelectWindow();
  486.  
  487.          /*
  488.           * Check to see that they didn't try to capture desktop window
  489.           */
  490.          hWndDesktop = GetDesktopWindow();
  491.          if (hWndSelect == hWndDesktop)
  492.          {
  493.             MessageBox(NULL, "Cannot capture Desktop window."
  494.                              "  Use 'Entire Screen' option to capture"
  495.                              " the entire screen.", szAppName,
  496.                        MB_ICONEXCLAMATION | MB_OK);
  497.             hDib = NULL;
  498.             break;
  499.          }
  500.  
  501.          /*
  502.           * Check to see that the hWnd is not NULL
  503.           */
  504.          if (!hWndSelect)
  505.          {
  506.             MessageBox(NULL, "Cannot capture that window!", szAppName,
  507.                        MB_ICONEXCLAMATION | MB_OK);
  508.             hDib = NULL;
  509.             break;
  510.          }
  511.  
  512.          /*
  513.           * Make sure it's not a hidden window.  Hmm, capturing a hidden
  514.           * window would certainly be a cool trick, wouldn't it?
  515.           */
  516.          if (!IsWindowVisible(hWndSelect))
  517.          {
  518.             MessageBox(NULL, "Window is not visible.  Can't capture",
  519.                        szAppName, MB_ICONEXCLAMATION | MB_OK);
  520.             hDib = NULL;
  521.             break;
  522.          }
  523.  
  524.          // Move window which was selected to top of Z-order for
  525.          // the capture, and make it redraw itself
  526.          SetWindowPos(hWndSelect, NULL, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOSIZE
  527.                       | SWP_NOMOVE);
  528.          UpdateWindow(hWndSelect);
  529.  
  530.          /*
  531.           * Get the caption
  532.           */
  533.          GetWindowText(hWndSelect, szWindowText, 100);
  534.  
  535.          /*
  536.           * Convert our OPTIONSTRUCT options to DIB API options
  537.           */
  538.          switch (lpOptionStruct->iOptionWindow)
  539.             {
  540.          case IDC_CLIENTAREAONLY:
  541.             wOption = PW_CLIENT;
  542.             break;
  543.  
  544.          case IDC_ENTIREWINDOW:
  545.          default:
  546.             wOption = PW_WINDOW;
  547.             break;
  548.             }
  549.          /*
  550.           * Call the DIB API function which captures the screen.  This
  551.           * will automatically allocate enough space for the DIB, fill
  552.           * in the proper fields in the DIB header, and return us a
  553.           * pointer to the memory containing the header, colortable,
  554.           * and bits of the DIB.
  555.           */
  556.          hDib = CopyWindowToDIB(hWndSelect, wOption);
  557.       }
  558.          break;
  559.  
  560.       default:
  561.          /*
  562.           * Oops, something went wrong
  563.           */
  564.          MessageBox(NULL, "Invalid Return value from Options DialogBox",
  565.                     szAppName, MB_ICONHAND | MB_OK);
  566.          break;
  567.          }
  568.       /*
  569.        * At this point, if hDib is NULL, then there was an error.  We should
  570.        * have already taken care of informing the user of the error.
  571.        */
  572.       if (!hDib)
  573.       {
  574.          GlobalUnlock(hOptionStruct);
  575.          DestroyDIB(hDib);
  576.          return;
  577.       }
  578.  
  579.       /*
  580.        * Now, process the destination information (e.g. to printer or file)
  581.        */
  582.  
  583.       /*
  584.        * See if the user checked the checkbox "Send to File"
  585.        */
  586.       if (lpOptionStruct->iOptionDest & OPTION_FILE)
  587.       {
  588.          WORD wReturn;
  589.          FARPROC lpfnDIALOGSMsgProc;
  590.  
  591.          // First, call up a modeless dialog box which tells that we are saving
  592.          // this to a file...
  593.  
  594.          if (!hModelessDlg)
  595.          {
  596.             lpfnDIALOGSMsgProc = MakeProcInstance((FARPROC)SavingDlgProc,
  597.                                                   ghInst);
  598.             hModelessDlg = CreateDialogParam(ghInst, (LPSTR)"Saving",
  599.                                              ghWndMain, lpfnDIALOGSMsgProc, (
  600.                                              DWORD)(LPSTR)lpOptionStruct->
  601.                                              szFileName);
  602.          }
  603.  
  604.          /*
  605.           * Call DIB API function which saves dib to file
  606.           */
  607.          wReturn = SaveDIB(hDib, (LPSTR)lpOptionStruct->szFileName);
  608.          DestroyWindow(hModelessDlg);
  609.          hModelessDlg = NULL;
  610.          if (wReturn)
  611.             MessageBox(NULL, "Error saving file", szAppName,
  612.                        MB_ICONEXCLAMATION | MB_OK);
  613.          FreeProcInstance(lpfnDIALOGSMsgProc);
  614.       }
  615.  
  616.       /*
  617.        * See if the user checked the box "Print to printer"
  618.        */
  619.       if (lpOptionStruct->iOptionDest & OPTION_PRINTER)
  620.       {
  621.          WORD wReturn;
  622.          WORD wOption;
  623.          WORD wX = 0, wY = 0;
  624.  
  625.          switch (lpOptionStruct->iOptionPrint)
  626.             {
  627.          case IDC_STRETCHTOPAGE:
  628.             wOption = PW_STRETCHTOPAGE;
  629.             break;
  630.  
  631.          case IDC_SCALE:
  632.             wOption = PW_SCALE;
  633.             wX = lpOptionStruct->iXScale;
  634.             wY = lpOptionStruct->iYScale;
  635.             break;
  636.  
  637.          default:
  638.          case IDC_BESTFIT:
  639.             wOption = PW_BESTFIT;
  640.             break;
  641.             }
  642.  
  643.          /*
  644.           * Send the bitmap to the printer, using the specified options
  645.           */
  646.          wReturn = PrintDIB(hDib, wOption, wX, wY, (LPSTR)szWindowText);
  647.          if (wReturn)
  648.          {
  649.             DIBError(wReturn);
  650.          }
  651.       }
  652.  
  653.       /*
  654.        * Clean up -- deallocate memory used for DIB
  655.        */
  656.       GlobalUnlock(hOptionStruct);
  657.       DestroyDIB(hDib);
  658.    }
  659. }
  660.  
  661.  
  662. /***********************************************************************
  663.  *
  664.  * SelectWindow()
  665.  *
  666.  * This function allows the user to select a window on the screen.  The
  667.  * cursor is changed to a custom cursor, then the user clicks on the title
  668.  * bar of a window to capture, and the handle to this window is returned.
  669.  *
  670.  **********************************************************************/
  671.  
  672.  
  673. HWND SelectWindow()
  674. {
  675.    HCURSOR hOldCursor;     // Handle to old cursor
  676.    POINT pt;               // Stores mouse position on a mouse click
  677.    HWND hWndClicked;       // Window we clicked on
  678.    MSG msg;
  679.  
  680.    /*
  681.     * Capture all mouse messages
  682.     */
  683.    SetCapture(ghWndMain);
  684.  
  685.    /*
  686.     * Load custom Cursor
  687.     */
  688.    hOldCursor = SetCursor(LoadCursor(ghInst, "SELECT"));
  689.  
  690.    /*
  691.     * Eat mouse messages until a WM_LBUTTONUP is encountered.
  692.     */
  693.    for (;;)
  694.    {
  695.       WaitMessage();
  696.       if (PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
  697.       {
  698.          if (msg.message == WM_LBUTTONUP)
  699.          {
  700.             /*
  701.              * Get mouse position
  702.              */
  703.             pt.x = LOWORD(msg.lParam);
  704.             pt.y = HIWORD(msg.lParam);
  705.  
  706.             /*
  707.              * Convert to screen coordinates
  708.              */
  709.             ClientToScreen(ghWndMain, &pt);
  710.  
  711.             /*
  712.              * Get Window that we clicked on
  713.              */
  714.             hWndClicked = WindowFromPoint(pt);
  715.  
  716.             /*
  717.              * If it's not a valid window, just return NULL
  718.              */
  719.             if (!hWndClicked)
  720.             {
  721.                ReleaseCapture();
  722.                SetCursor(hOldCursor);
  723.                return NULL;
  724.             }
  725.             break;
  726.          }
  727.       }
  728.       else
  729.          continue;
  730.    }
  731.    ReleaseCapture();
  732.    SetCursor(hOldCursor);
  733.    return (hWndClicked);
  734. }
  735.  
  736.  
  737. /***********************************************************************
  738.  *
  739.  * RubberBandScreen()
  740.  *
  741.  * This function allows the user to rubber-band a portion of the screen.
  742.  * When the left button is released, the rect that the user selected
  743.  * (in screen coordinates) is returned in lpRect.
  744.  *
  745.  **********************************************************************/
  746.  
  747.  
  748. void RubberBandScreen(LPRECT lpRect)
  749. {
  750.    POINT pt;           // Temporary POINT
  751.    MSG msg;            // Used in our PeekMessage() loop
  752.    POINT ptOrigin;     // Point where the user pressed left mouse button down
  753.    RECT rcClip;        // Current selection
  754.    HDC hScreenDC;      // DC to the screen (so we can draw on it)
  755.    HCURSOR hOldCursor; // Saves old cursor
  756.    BOOL bCapturing = FALSE;    // TRUE if we are rubber-banding
  757.  
  758.    hScreenDC = CreateDC("DISPLAY", NULL, NULL, NULL);
  759.  
  760.    /*
  761.     * Make cursor our custom cursor
  762.     */
  763.    hOldCursor = SetCursor(LoadCursor(NULL, IDC_CROSS));
  764.  
  765.    /*
  766.     * Capture all mouse messages
  767.     */
  768.    SetCapture(ghWndMain);
  769.  
  770.    /* Eat mouse messages until a WM_LBUTTONUP is encountered. Meanwhile
  771.     * continue to draw a rubberbanding rectangle and display it's dimensions
  772.     */
  773.    for (;;)
  774.    {
  775.       WaitMessage();
  776.       if (PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
  777.       {
  778.          /*
  779.           * If the message is a WM_LBUTTONDOWN, begin drawing the
  780.           * rubber-band box.
  781.           */
  782.          if (msg.message == WM_LBUTTONDOWN)
  783.          {
  784.             /*
  785.              * User pressed left button, initialize selection
  786.              * Set origin to current mouse position (in window coords)
  787.              */
  788.             ptOrigin.x = LOWORD(msg.lParam);
  789.             ptOrigin.y = HIWORD(msg.lParam);
  790.  
  791.             /*
  792.              * Convert to screen coordinates
  793.              */
  794.             ClientToScreen(ghWndMain, &ptOrigin);
  795.  
  796.             /*
  797.              * rcClip is the current rectangle that the user
  798.              * has selected.  Since user just pressed left button down,
  799.              * initialize this rect very small
  800.              */
  801.             rcClip.left = rcClip.right = ptOrigin.x;
  802.             rcClip.top = rcClip.bottom = ptOrigin.y;
  803.             NormalizeRect(&rcClip);     // Make sure it is a normal rect
  804.             DrawSelect(hScreenDC, TRUE, &rcClip); // Draw the rubber-band box
  805.             bCapturing = TRUE;
  806.          }
  807.          /*
  808.           * Any messages that make it into the next statement are mouse
  809.           * messages, and we are capturing, so let's update the rubber-band
  810.           * box
  811.           */
  812.          if (bCapturing)
  813.          {
  814.             DrawSelect(hScreenDC, FALSE, &rcClip);    // erase old rubber-band
  815.             rcClip.left = ptOrigin.x;     // Update rect with new mouse info
  816.             rcClip.top = ptOrigin.y;
  817.             pt.x = LOWORD(msg.lParam);
  818.             pt.y = HIWORD(msg.lParam);
  819.  
  820.             /*
  821.              * Convert to screen coordinates
  822.              */
  823.             ClientToScreen(ghWndMain, &pt);
  824.             rcClip.right = pt.x;
  825.             rcClip.bottom = pt.y;
  826.             NormalizeRect(&rcClip);
  827.             DrawSelect(hScreenDC, TRUE, &rcClip); // And draw the new rubber-band
  828.          }
  829.  
  830.          // If the message is WM_LBUTTONUP, then we stop the selection
  831.          // process.
  832.          if (msg.message == WM_LBUTTONUP)
  833.          {
  834.             DrawSelect(hScreenDC, FALSE, &rcClip);    // erase rubber-band
  835.             SetCursor(hOldCursor);
  836.             break;
  837.          }
  838.       }
  839.       else
  840.          continue;
  841.    }
  842.    ReleaseCapture();
  843.    DeleteDC(hScreenDC);
  844.  
  845.    /*
  846.     * Assign rect user selected to lpRect parameter
  847.     */
  848.    CopyRect(lpRect, &rcClip);
  849. }
  850.  
  851.  
  852. /****************************************************************************
  853.  *
  854.  * DrawSelect
  855.  *
  856.  * Draws the selected clip rectangle with its dimensions on the DC
  857.  * This code is taken from DIBVIEW.
  858.  *
  859.  ****************************************************************************/
  860.  
  861.  
  862. void DrawSelect(HDC hdc,           // DC to draw on
  863.                 BOOL fDraw,        // TRUE to draw, FALSE to erase
  864.                 LPRECT lprClip)    // rect to draw
  865. {
  866.    char sz[80];
  867.    DWORD dw;
  868.    int x, y, len, dx, dy;
  869.    HDC hdcBits;
  870.    HBITMAP hbm;
  871.    RECT rcClip;
  872.  
  873.    rcClip = *lprClip;
  874.    if (!IsRectEmpty(&rcClip))
  875.    {
  876.  
  877.       /* If a rectangular clip region has been selected, draw it */
  878.       PatBlt(hdc, rcClip.left, rcClip.top, rcClip.right - rcClip.left, 1,
  879.              DSTINVERT);
  880.       PatBlt(hdc, rcClip.left, rcClip.bottom, 1, -(rcClip.bottom - rcClip.top)
  881.              , DSTINVERT);
  882.       PatBlt(hdc, rcClip.right - 1, rcClip.top, 1, rcClip.bottom - rcClip.top,
  883.              DSTINVERT);
  884.       PatBlt(hdc, rcClip.right, rcClip.bottom - 1, -(rcClip.right -
  885.              rcClip.left), 1, DSTINVERT);
  886.  
  887.       /* Format the dimensions string ...*/
  888.       wsprintf(sz, "%dx%d", rcClip.right - rcClip.left, rcClip.bottom -
  889.                rcClip.top);
  890.       len = lstrlen(sz);
  891.  
  892.       /* ... and center it in the rectangle */
  893.       dw = GetTextExtent(hdc, sz, len);
  894.       dx = LOWORD (dw);
  895.       dy = HIWORD (dw);
  896.       x = (rcClip.right + rcClip.left - dx) / 2;
  897.       y = (rcClip.bottom + rcClip.top - dy) / 2;
  898.       hdcBits = CreateCompatibleDC(hdc);
  899.       SetTextColor(hdcBits, 0xFFFFFFL);
  900.       SetBkColor(hdcBits, 0x000000L);
  901.  
  902.       /* Output the text to the DC */
  903.       if (hbm = CreateBitmap(dx, dy, 1, 1, NULL))
  904.       {
  905.          hbm = SelectObject(hdcBits, hbm);
  906.          ExtTextOut(hdcBits, 0, 0, 0, NULL, sz, len, NULL);
  907.          BitBlt(hdc, x, y, dx, dy, hdcBits, 0, 0, SRCINVERT);
  908.          hbm = SelectObject(hdcBits, hbm);
  909.          DeleteObject(hbm);
  910.       }
  911.       DeleteDC(hdcBits);
  912.    }
  913. }
  914.  
  915.  
  916. /****************************************************************************
  917.  *                                                                          *
  918.  *  FUNCTION   : NormalizeRect(RECT *prc)                                   *
  919.  *                                                                          *
  920.  *  PURPOSE    : If the rectangle coordinates are reversed, swaps them      *
  921.  *                                                                          *
  922.  ****************************************************************************/
  923.  
  924.  
  925. void PASCAL NormalizeRect(LPRECT prc)
  926. {
  927.    if (prc->right < prc->left)
  928.       SWAP(prc->right,prc->left);
  929.    if (prc->bottom < prc->top)
  930.       SWAP(prc->bottom,prc->top);
  931. }
  932.  
  933.